package com.basic.library.brvah;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ObservableArrayList;
import androidx.databinding.ViewDataBinding;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import com.basic.library.R;
import com.chad.library.adapter.base.BaseMultiItemQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.chad.library.adapter.base.entity.IExpandable;
import com.chad.library.adapter.base.entity.MultiItemEntity;
import com.chad.library.adapter.base.loadmore.LoadMoreView;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author jiangyun
 * @description 多布局适配器(支持列表 + 网格混排)
 * 混排时！！！getItemType < 0 ,是 整行显示
 * @time 创建时间：2018/4/2 13:27
 */
public abstract class MyBaseMultiItemQuickAdapter<T extends MultiItemEntity, K extends MBaseViewHolder>
        extends BaseMultiItemQuickAdapter<T, K> {

    private View emptyView;
    private int emptyId = R.layout.base_footer;
    private int[] mVariableId;
    private int[] types;
    private ArrayList<HeaderOrFooterInfo> headerInfo = new ArrayList<>();
    private ArrayList<HeaderOrFooterInfo> footerInfo = new ArrayList<>();

    public MyBaseMultiItemQuickAdapter(int[] layoutResId) {
        this(new ArrayList<T>(), layoutResId);
    }

    /**
     * @param layoutResId
     * @param mVariableId
     */
    public MyBaseMultiItemQuickAdapter(int[] layoutResId, int[] mVariableId) {
        this(new ArrayList<T>(), layoutResId);
        this.mVariableId = mVariableId;
    }

    public MyBaseMultiItemQuickAdapter(int[] types, int[] layoutResId, int[] mVariableId) {
        this(types, new ArrayList<T>(), layoutResId);
        this.mVariableId = mVariableId;
        this.types = types;
    }

    //非databinding
    public MyBaseMultiItemQuickAdapter(List<T> data, int[] layoutResId) {
        super(data);
//        ItemType默认从0开始（0,1,2,3...）
        for (int i = 0; i < layoutResId.length; i++) {
            addItemType(i, layoutResId[i]);
        }
        setHeaderFooterEmpty(true, true);
    }

    //非databinding
    public MyBaseMultiItemQuickAdapter(int[] types, List<T> data, int[] layoutResId) {
        super(data);
        for (int i = 0; i < types.length; i++) {
            addItemType(types[i], layoutResId[i]);
        }
        setHeaderFooterEmpty(true, true);
    }

    @Override
    protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
        ViewDataBinding binding = null;
        if (mVariableId != null)
            binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutResId, parent, false);
        if (binding != null && mVariableId != null) {
            MBaseViewHolder holder = new MBaseViewHolder(binding);
            holder.setBinding(binding);
            return (K) holder;
        } else {
            return super.createBaseViewHolder(parent, layoutResId);
        }
    }

    protected K createBaseViewHolder(View view) {
        Class temp = getClass();
        Class z = null;
        while (z == null && null != temp) {
            z = getInstancedGenericKClass(temp);
            temp = temp.getSuperclass();
        }
        K k;
        // 泛型擦除会导致z为null
        if (z == null) {
            k = (K) new MBaseViewHolder(view);
        } else {
            k = createGenericKInstance(z, view);
        }
        return k != null ? k : (K) new MBaseViewHolder(view);
    }

    private K createGenericKInstance(Class z, View view) {
        try {
            Constructor constructor;
            // inner and unstatic class
            if (z.isMemberClass() && !Modifier.isStatic(z.getModifiers())) {
                constructor = z.getDeclaredConstructor(getClass(), View.class);
                constructor.setAccessible(true);
                return (K) constructor.newInstance(this, view);
            } else {
                constructor = z.getDeclaredConstructor(View.class);
                constructor.setAccessible(true);
                return (K) constructor.newInstance(view);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    private Class getInstancedGenericKClass(Class z) {
        Type type = z.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] types = ((ParameterizedType) type).getActualTypeArguments();
            for (Type temp : types) {
                if (temp instanceof Class) {
                    Class tempClass = (Class) temp;
                    if (BaseViewHolder.class.isAssignableFrom(tempClass)) {
                        return tempClass;
                    }
                } else if (temp instanceof ParameterizedType) {
                    Type rawType = ((ParameterizedType) temp).getRawType();
                    if (rawType instanceof Class && BaseViewHolder.class.isAssignableFrom((Class<?>) rawType)) {
                        return (Class<?>) rawType;
                    }
                }
            }
        }
        return null;
    }

    @Override
    protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
        return super.onCreateDefViewHolder(parent, viewType);
    }

    @Override
    protected int getDefItemViewType(int position) {
        return mData.get(position).getItemType();
    }

    @Override
    protected boolean isFixedViewType(int type) {
        return super.isFixedViewType(type) || type < 0;
    }

    @Override
    public void onBindViewHolder(K holder, int position) {
        if (holder.binding != null && mVariableId != null) {
            int variableId = 0;
            if (types == null)
                variableId = mVariableId[holder.getItemViewType()];
            else {
                for (int i = 0; i < types.length; i++) {
                    if (holder.getItemViewType() == types[i])
                        variableId = mVariableId[i];
                }
            }
            holder.binding.setVariable(variableId, getData().get(position - getHeaderLayoutCount()));
            holder.binding.executePendingBindings();
        }
        super.onBindViewHolder(holder, position);
    }

    public void initData(List<T> data) {
        if (mObservableArrayList == null && data instanceof ObservableArrayList) {
            mObservableArrayList = (ObservableArrayList) data;
        }
        if (data == null)
            data = new ArrayList<>();
        if (page == 1) {
            setNewData(data, needScrollToTop);
        } else {
            addData(data);
        }
    }

    @Override
    public void setNewData(List<T> data) {
        super.setNewData(data);
        if (!isLoadMoreEnable() && mObservableArrayList != null)
            page = -1; //不分页时 ObservableArrayList增删改查需要这行
        setLoadMoreFoot(getData().size());
        isClear = false;
        needScrollToTop = true;
        if (!noEmptyView)
            if (getEmptyViewCount() == 0)
                addEmptyView(data);
            else
                emptyViewShow(true);
    }

    public void setNewData(List<T> data, boolean toTop) {
        setNewData(data);
        if (toTop && getRecyclerView() != null)
            getRecyclerView().scrollToPosition(0);
    }

    private void addEmptyView(List<T> data) {
        if (emptyView == null && getRecyclerView() != null)
            emptyView = View.inflate(getRecyclerView().getContext(), emptyId, null);
        if (/*data.size() == 0 && */emptyView != null) {
            setEmptyView(emptyView);
        }
    }

    //版本2.9.33
    @Override
    public void addData(@NonNull Collection<? extends T> newData) {
        super.addData(newData);
        setLoadMoreFoot(newData.size());
    }

    //databinding通过ListFactory监听，不用继续super.addData(newData);数据会重复添加
    public void addDataNo(int newDataSize) {
        notifyItemRangeInserted(mData.size() - newDataSize + getHeaderLayoutCount(), newDataSize);
        final int dataSize = mData == null ? 0 : mData.size();
        if (dataSize == newDataSize) {
            notifyDataSetChanged();
        }
        setLoadMoreFoot(newDataSize);
    }

    private boolean isNoMore = false;

    public void setLoadMoreFoot(int newDataSize) {
        chargeRefresh(false);
        if (isLoadMoreEnable())
            if (newDataSize != pageSize) {
                isNoMore = true;
                this.loadMoreEnd(this.getData().size() < oSize); //显示没有更多（第2页开始）
            } else {
                isNoMore = false;
                page++;
                insertLoadMoreView();
                this.loadMoreComplete();
            }
        if (iRefreshByPaging) {
            iRefreshByPaging = false;
            page = pageSize / oSize;
            pageSize = oSize;
        }
    }

    void insertLoadMoreView() {
        if (getLoadMoreViewCount() == 0)
            //adapter重新插入LoadMoreView
            try {
                Class c = getClass().getSuperclass().getSuperclass();
                Field field = c.getDeclaredField("mLoadMoreView");
                field.setAccessible(true);
                LoadMoreView lmv = (LoadMoreView) field.get(this);
                lmv.setLoadMoreEndGone(false);
            } catch (Exception e) {
                e.printStackTrace();
            }
    }

    //页数和尺寸（分页）
    public int oSize = 20;
    public int page = 1, pageSize = oSize;

    /**
     * pageSize的赋值必须通过 initPageSize，为了确保 oSize初始化后不变
     */
    public void initPageSize(int size) {
        pageSize = oSize = size;
    }

    public void setEmptyBackGround(Context activity, int emptyId) {
        this.emptyId = emptyId != 0 ? emptyId : R.layout.base_footer;
    }

    public void setFooterView(int mVariableId, int layoutResId, Object data) {
        footerInfo.add(new HeaderOrFooterInfo(0, mVariableId, layoutResId, data, LinearLayout.VERTICAL));
    }

    public void setFooterView(int index, int mVariableId, int layoutResId, Object data) {
        footerInfo.add(new HeaderOrFooterInfo(index, mVariableId, layoutResId, data, LinearLayout.VERTICAL));
    }

    @SuppressLint("CheckResult")
    public void setFooterView(int index, int mVariableId, int layoutResId, Object data, int orientation) {
        footerInfo.add(new HeaderOrFooterInfo(index, mVariableId, layoutResId, data, orientation));
    }

    private void addFooterView(HeaderOrFooterInfo info) {
        if (getRecyclerView() != null) {
            ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getRecyclerView().getContext()), info.layoutResId, getRecyclerView(), false);
            binding.setVariable(info.mVariableId, info.data);
            setFooterView(binding.getRoot(), info.index, info.orientation);
        }
    }

    public void setHeaderView(int mVariableId, int layoutResId, Object data) {
        headerInfo.add(new HeaderOrFooterInfo(0, mVariableId, layoutResId, data, LinearLayout.VERTICAL));
    }

    public void setHeaderView(int index, int mVariableId, int layoutResId, Object data) {
        headerInfo.add(new HeaderOrFooterInfo(index, mVariableId, layoutResId, data, LinearLayout.VERTICAL));
    }

    @SuppressLint("CheckResult")
    public void setHeaderView(int index, int mVariableId, int layoutResId, Object data, int orientation) {
        headerInfo.add(new HeaderOrFooterInfo(index, mVariableId, layoutResId, data, orientation));
    }

    public void addHeaderView(HeaderOrFooterInfo info) {
        if (getRecyclerView() != null) {
            ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater.from(getRecyclerView().getContext()), info.layoutResId, getRecyclerView(), false);
            binding.setVariable(info.mVariableId, info.data);
            setHeaderView(binding.getRoot(), info.index, info.orientation);
            getRecyclerView().smoothScrollToPosition(0);
        }
    }

    @Override
    public void bindToRecyclerView(RecyclerView recyclerView) {
//        if (getRecyclerView() == null)
        super.bindToRecyclerView(recyclerView);
        chargeRefresh(true);
        for (HeaderOrFooterInfo info : footerInfo) {
            addFooterView(info);
        }
        for (HeaderOrFooterInfo info : headerInfo) {
            addHeaderView(info);
        }
    }

    public void emptyViewShow(boolean show) {
        if (getEmptyView() != null)
            getEmptyView().setVisibility(show ? View.VISIBLE : View.GONE);
    }

    boolean noEmptyView = false;
    ObservableArrayList mObservableArrayList;

    /**
     * 不显示空底图
     */
    public void noEmptyView() {
        noEmptyView = true;
    }

    public boolean isClear, needScrollToTop = true, iRefreshByPaging;

    /**
     * 重置参数
     * 被动刷新分页的列表，item位置不变，只要把pageSize变大到当前page页数对应的个数
     * （比 pageSize 设置成int最大值好）
     * 被动刷新完后，pageSize重新设置回原始值(在 setLoadMoreFoot 方法中设置原始值)
     */
    public void refreshByPaging() {
        iRefreshByPaging = true;
        if (page > 1) {
            if (isNoMore)
                pageSize = page * pageSize;
            else
                pageSize = (page - 1) * pageSize;
        }
        clearData();
    }

    public void clearData() {
        if (mObservableArrayList == null) {
            page = 1;
            return;
        }
        isClear = true;
        page = 1;
        mObservableArrayList.clear();
        emptyViewShow(false);
    }

    /**
     * databinding使用
     */
    public void addAll(List data) {
        if (mObservableArrayList == null) {
            initData(data);
            return;
        }
        mObservableArrayList.addAll(data);
        emptyViewShow(mObservableArrayList.size() == 0);
    }

    /**
     * @param data
     * @param toTop 默认第一页（page=1）会滑动到列表顶部，如果刷新不需要滑动到顶部，手动置false
     */
    public void addAll(List data, boolean toTop) {
        this.needScrollToTop = toTop;
        addAll(data);
    }

    public int totalCount = 0;

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public void notifyItemChangedMy(int position) {
        notifyItemChanged(position + getHeaderLayoutCount());
    }

    /**
     * SwipeRefreshLayout最多2层，够用了
     * 操作 SwipeRefreshLayout 的刷新动画
     * （只能完美解决消失的动画，开始的动画）
     * （开始的动画需要手动chargeRefresh(true) ，或者借助BaseFragmentKt BaseActivityKt中的覆写）
     * <p>
     * 如果没有BaseFragmentKt BaseActivityKt，需要在刷新接口前手动加 chargeRefresh(true)
     */
    public void chargeRefresh(boolean refresh) {
        RecyclerView recyclerView = getRecyclerView();
        if (recyclerView != null) {
            if (recyclerView.getParent() instanceof SwipeRefreshLayout) {
                SwipeRefreshLayout s = ((SwipeRefreshLayout) recyclerView.getParent());
                if (s.isEnabled())
                    s.setRefreshing(refresh);
            } else if (recyclerView.getParent().getParent() instanceof SwipeRefreshLayout) {
                SwipeRefreshLayout s = ((SwipeRefreshLayout) recyclerView.getParent().getParent());
                if (s.isEnabled())
                    s.setRefreshing(refresh);
            }
        }
    }

    /**
     * 三方库里的有问题
     *
     * @param item
     * @return
     */
    private int getItemPosition(T item) {
        int position = 0;
        for (int i = 0; i < mData.size(); i++) {
            if (mData.get(i) == item) {
                position = i;
                break;
            }
        }
        return item != null && mData != null && !mData.isEmpty() ? position : -1;
    }

    @Override
    public int getParentPosition(@NonNull T item) {
        int position = getItemPosition(item);
        if (position == -1) {
            return -1;
        }

        // if the item is IExpandable, return a closest IExpandable item position whose level smaller than this.
        // if it is not, return the closest IExpandable item position whose level is not negative
        int level;
        if (item instanceof IExpandable) {
            level = ((IExpandable) item).getLevel();
        } else {
            level = Integer.MAX_VALUE;
        }
        if (level == 0) {
            return position;
        } else if (level == -1) {
            return -1;
        }

        for (int i = position; i >= 0; i--) {
            T temp = mData.get(i);
            if (temp instanceof IExpandable) {
                IExpandable expandable = (IExpandable) temp;
                if (expandable.getLevel() >= 0 && expandable.getLevel() < level) {
                    return i;
                }
            }
        }
        return -1;
    }
}